﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace ZFramework.Runtime
{
    public partial class ObjectPoolMgr
    {
        /// <summary>
        /// 释放对象筛选函数。
        /// </summary>
        /// <typeparam name="T">对象类型。</typeparam>
        /// <param name="candidateObjects">要筛选的对象集合。</param>
        /// <param name="toReleaseCount">需要释放的对象数量。</param>
        /// <param name="expireTime">对象过期参考时间。</param>
        /// <returns>经筛选需要释放的对象集合。</returns>
        public delegate List<T> ReleaseObjectFilterCallback<T>(List<T> candidateObjects, int toReleaseCount, DateTime expireTime) where T : ObjectBase;
        
        public sealed class ObjectPool<T> : ObjectPoolBase where T : ObjectBase
        {
            private readonly FrameworkMultiDictionary<string, InternalObject<T>> _internalObjects;
            private readonly Dictionary<object, InternalObject<T>> _internalObjectMap;
            private readonly ReleaseObjectFilterCallback<T> _defaultReleaseBundleFilterCallback;
            private readonly List<T> _cachedCanReleaseObjects;
            private readonly List<T> _cachedToReleaseObjects;
            private readonly bool _allowMultiSpawn; //是否允许多次获取：Asset，Material，Texture等都可以多次获取；GameObject只能单次获取
            private float _autoReleaseInterval;
            private int _capacity;
            private float _expireTime;
            private float _autoReleaseTime;
            private int _priority;

            public override int Count
            {
                get => _internalObjectMap.Count;
            }

            public override int CanReleaseCount
            {
                get
                {
                    GetCanReleaseObjects(_cachedCanReleaseObjects);
                    return _cachedCanReleaseObjects.Count;
                }
            }

            public override bool AllowMultiSpawn => _allowMultiSpawn;

            public override float AutoReleaseInterval
            {
                get => _autoReleaseInterval;
                set => _autoReleaseInterval = value;
            }

            public override int Capacity
            {
                get => _capacity;
                set => _capacity = value;
            }

            public override float ExpireTime
            {
                get => _expireTime;
                set => _expireTime = value;
            }


            /// <summary>
            /// 获取或设置对象池的优先级。
            /// </summary>
            public override int Priority
            {
                get
                {
                    return _priority;
                }
                set
                {
                    _priority = value;
                }
            }
            
            /// <summary>
            /// 获取对象池对象类型。
            /// </summary>
            public override Type ObjectType
            {
                get
                {
                    return typeof(T);
                }
            }

            public ObjectPool(string name, bool allowMultiSpawn, float autoReleaseInterval, int capacity,
                float expireTime) : base(name)
            {
                _internalObjects = new FrameworkMultiDictionary<string, InternalObject<T>>();
                _internalObjectMap = new Dictionary<object, InternalObject<T>>();
                _cachedCanReleaseObjects = new List<T>();
                _cachedToReleaseObjects = new List<T>();
                _allowMultiSpawn = allowMultiSpawn;
                _autoReleaseInterval = autoReleaseInterval;
                _capacity = capacity;
                _expireTime = expireTime;
                _autoReleaseTime = 0f;
                _priority = 0;
                _defaultReleaseBundleFilterCallback = DefaultReleaseObjectFilterCallback;
            }

            //将obj注册到系统中，统一管理
            public void Register(T obj, bool spawned)
            {
                if (obj == null)
                    return;
                InternalObject<T> internalObject = InternalObject<T>.Create(obj, spawned);
                _internalObjects.Add(obj.Name, internalObject);
                _internalObjectMap.Add(obj.Instance, internalObject);

                if (Count > _capacity)
                {
                    Release();
                }
            }

            public T Spawn(string name)
            {
                if (_internalObjects.TryGetValue(name, out var objectRange))
                {
                    foreach (var internalObject in objectRange)
                    {
                        if (_allowMultiSpawn || !internalObject.IsInUse)
                        {
                            return internalObject.Spawn();
                        }
                    }
                }

                return null;
            }

            public void UnSpawn(T obj)
            {
                if (obj == null)
                {
                    return;
                }

                UnSpawn(obj.Instance);
            }

            public void UnSpawn(object target)
            {
                if (target == null)
                    return;
                var internalObject = GetObject(target);
                if (internalObject != null)
                {
                    internalObject.UnSpawn();
                    if (Count > _capacity && internalObject.SpawnCount <= 0)
                    {
                        Release();
                    }
                }
                else
                {
                    Log.Error("不能找到internalObject");
                }
            }
            
            public override void Update()
            {
                _autoReleaseTime += Time.unscaledDeltaTime;
                if (_autoReleaseTime < _autoReleaseInterval)
                {
                    return;
                }

                Release();
            }
            
            public override void Release()
            {
                Release(Count - _capacity, _defaultReleaseBundleFilterCallback);
            }
            
            /// <summary>
            /// 释放对象池中的可释放对象。
            /// </summary>
            /// <param name="toReleaseCount">尝试释放对象数量。</param>
            public void Release(int toReleaseCount)
            {
                Release(toReleaseCount, _defaultReleaseBundleFilterCallback);
            }

            /// <summary>
            /// 释放对象池中的可释放对象。
            /// </summary>
            /// <param name="releaseObjectFilterCallback">释放对象筛选函数。</param>
            public void Release(ReleaseObjectFilterCallback<T> releaseObjectFilterCallback)
            {
                Release(Count - _capacity, releaseObjectFilterCallback);
            }
            
            public void Release(int toReleaseCount, ReleaseObjectFilterCallback<T> releaseObjectFilterCallback)
            {
                if (releaseObjectFilterCallback == null)
                {
                    throw new Exception("Release object filter callback is invalid.");
                }

                if (toReleaseCount < 0)
                {
                    toReleaseCount = 0;
                }

                DateTime expireTime = DateTime.MinValue;
                if (_expireTime < float.MaxValue)
                {
                    expireTime = DateTime.UtcNow.AddSeconds(-_expireTime);
                }

                _autoReleaseTime = 0f;
                GetCanReleaseObjects(_cachedCanReleaseObjects);
                List<T> toReleaseObjects = releaseObjectFilterCallback(_cachedCanReleaseObjects, toReleaseCount, expireTime);
                if (toReleaseObjects == null || toReleaseObjects.Count <= 0)
                {
                    return;
                }

                foreach (T toReleaseObject in toReleaseObjects)
                {
                    ReleaseObject(toReleaseObject);
                }
            }
            
            private void GetCanReleaseObjects(List<T> results)
            {
                if (results == null)
                {
                    throw new Exception("Results is invalid.");
                }

                results.Clear();
                foreach (KeyValuePair<object, InternalObject<T>> objectInMap in _internalObjectMap)
                {
                    InternalObject<T> internalObject = objectInMap.Value;
                    if (internalObject.IsInUse || internalObject.Locked)
                    {
                        continue;
                    }

                    results.Add(internalObject.Peek());
                }
            }
            
            public bool ReleaseObject(T obj)
            {
                if (obj == null)
                {
                    throw new Exception("Object is invalid.");
                }

                return ReleaseObject(obj.Instance);
            }
            
            public bool ReleaseObject(object target)
            {
                if (target == null)
                {
                    throw new Exception("Target is invalid.");
                }

                InternalObject<T> internalObject = GetObject(target);
                if (internalObject == null)
                {
                    return false;
                }

                if (internalObject.IsInUse || internalObject.Locked)
                {
                    return false;
                }

                _internalObjects.Remove(internalObject.Name, internalObject);
                _internalObjectMap.Remove(internalObject.Peek().Instance);

                internalObject.Release();
                ReferencePool.Release(internalObject);
                return true;
            }

            public InternalObject<T> GetObject(object target)
            {
                if (_internalObjectMap.TryGetValue(target, out var obj))
                {
                    return obj;
                }

                return null;
            }

            private List<T> DefaultReleaseObjectFilterCallback(List<T> candidateObjects, int toReleaseCount, DateTime expireTime)
            {
                _cachedToReleaseObjects.Clear();

                if (expireTime > DateTime.MinValue)
                {
                    for (int i = candidateObjects.Count - 1; i >= 0; i--)
                    {
                        if (candidateObjects[i].LastUseTime <= expireTime)
                        {
                            _cachedToReleaseObjects.Add(candidateObjects[i]);
                            candidateObjects.RemoveAt(i);
                            continue;
                        }
                    }

                    toReleaseCount -= _cachedToReleaseObjects.Count;
                }

                for (int i = 0; toReleaseCount > 0 && i < candidateObjects.Count; i++)
                {
                    _cachedToReleaseObjects.Add(candidateObjects[i]);
                    toReleaseCount--;
                }

                return _cachedToReleaseObjects;
            }
            
            /// <summary>
            /// 释放对象池中的所有未使用对象。
            /// </summary>
            public override void ReleaseAllUnused()
            {
                _autoReleaseTime = 0f;
                GetCanReleaseObjects(_cachedCanReleaseObjects);
                foreach (T toReleaseObject in _cachedCanReleaseObjects)
                {
                    ReleaseObject(toReleaseObject);
                }
            }
            
            /// <summary>
            /// 获取所有对象信息。
            /// </summary>
            /// <returns>所有对象信息。</returns>
            public override ObjectInfo[] GetAllObjectInfos()
            {
                List<ObjectInfo> results = new List<ObjectInfo>();
                foreach (KeyValuePair<string, FrameworkLinkedListRange<InternalObject<T>>> objectRanges in _internalObjects)
                {
                    foreach (InternalObject<T> internalObject in objectRanges.Value)
                    {
                        results.Add(new ObjectInfo(internalObject.Name, internalObject.Locked, internalObject.Priority, internalObject.LastUseTime, internalObject.SpawnCount));
                    }
                }

                return results.ToArray();
            }

        }
    }
}